package com.fs.controller.general;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fs.global.config.WxPayConfig;
import com.fs.service.PurchaseService;
import com.fs.service.bo.purchase.BoPurchase;
import com.fs.service.bo.purchase.Status;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Log4j2
@RestController
public class WeiXinCallBackController {

    private final PurchaseService purchaseService;
    private final WxPayConfig wxPayConfig;


    public WeiXinCallBackController(PurchaseService purchaseService, WxPayConfig wxPayConfig) {
        this.purchaseService = purchaseService;
        this.wxPayConfig = wxPayConfig;
    }


    @PostMapping("/wxPay/callback")
    public void callBackPay(HttpServletRequest request, HttpServletResponse response) throws GeneralSecurityException, IOException {
        String serialNo = request.getHeader("Wechatpay-Serial");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String wxSig = request.getHeader("Wechatpay-Signature");
        String body;
        try {
            body = request.getReader().lines().collect(Collectors.joining());
        } catch (IOException e) {
            responseError(response, "读取body异常");
            return;
        }

        String msg = Stream.of(timestamp, nonce, body)
                .collect(Collectors.joining("\n", "", "\n"));
        boolean sigIsTrue = wxPayConfig.getVerifier().verify(serialNo, msg.getBytes(StandardCharsets.UTF_8), wxSig);

        if (!sigIsTrue) {
            responseError(response, "签名非法");
            return;
        }

        JSONObject jsonObject = JSON.parseObject(body);
        JSONObject resource = jsonObject.getJSONObject("resource");
        byte[] nonceBytes = resource.getString("nonce").getBytes(StandardCharsets.UTF_8);
        byte[] associatedBytes = resource.getString("associated_data").getBytes(StandardCharsets.UTF_8);
        String cipherText = resource.getString("ciphertext");
        AesUtil aesUtil = new AesUtil(wxPayConfig.getV3Key().getBytes());
        String payResult = aesUtil.decryptToString(associatedBytes, nonceBytes, cipherText);
        log.info("支付通知解密:" + payResult);

        JSONObject notifyObj = JSON.parseObject(payResult);
        String trade_state = notifyObj.getString("trade_state");
        String out_trade_no = notifyObj.getString("out_trade_no");
        String transaction_id = notifyObj.getString("transaction_id");

        BoPurchase purchase = purchaseService.getPurchaseByTradeNo(out_trade_no);

        if ((purchase.getState() == Status.NotPayed || purchase.getState() == Status.Expired)
                && trade_state.equals("SUCCESS")) {
            purchaseService.setPurchasePayComplete(purchase.getId(), transaction_id);
        }
        responseSuccess(response);
    }

    private void responseError(HttpServletResponse response, String errMsg) {
        log.error(errMsg);
        response.setStatus(500);
        response.setCharacterEncoding("UTF-8");
        try {
            response.getWriter().print(buildFailResp(errMsg));
        } catch (IOException e) {
            log.error("支付通知应答异常");
            e.printStackTrace();
        }
    }

    private void responseSuccess(HttpServletResponse response) {
        response.setStatus(200);
        response.setCharacterEncoding("UTF-8");
        try {
            response.getWriter().print(buildSuccessResp());
        } catch (IOException e) {
            log.error("支付通知应答异常");
            e.printStackTrace();
        }
    }

    private String buildSuccessResp() {
        CallBackResp resp = new CallBackResp(true, "成功");
        return JSON.toJSONString(resp);
    }

    private String buildFailResp(String errMsg) {
        CallBackResp resp = new CallBackResp(false, errMsg);
        return JSON.toJSONString(resp);
    }


    @Getter
    private static class CallBackResp {
        private final String code;
        private final String message;

        CallBackResp(boolean isSuccess, String message) {
            if (isSuccess) {
                code = "SUCCESS";
            } else {
                code = "ERROR";
            }
            this.message = message;
        }
    }
}
